#include "sqpcheader.h"
#include "sqvm.h"
#include "sqtable.h"
#include "sqclass.h"
#include "sqclosure.h"

SQClass::SQClass( SQSharedState *ss, SQClass *base ) {
  _base = base;
  _typetag = 0;
  _hook = NULL;
  _udsize = 0;
  _metamethods.resize( MT_LAST );
  if( _base ) {
    _defaultvalues.copy( base->_defaultvalues );
    _methods.copy( base->_methods );
    _metamethods.copy( base->_metamethods );
    __ObjAddRef( _base );
  }
  _members = base ? base->_members->Clone() : SQTable::Create( ss, 0 );
  __ObjAddRef( _members );
  _locked = false;
  INIT_CHAIN();
  ADD_TO_CHAIN( &_sharedstate->_gc_chain, this );
}

void SQClass::Finalize() {
  _attributes = _null_;
  _defaultvalues.resize( 0 );
  _methods.resize( 0 );
  _metamethods.resize( 0 );
  __ObjRelease( _members );
  if( _base ) {
    __ObjRelease( _base );
  }
}

SQClass::~SQClass() {
  REMOVE_FROM_CHAIN( &_sharedstate->_gc_chain, this );
  Finalize();
}

bool SQClass::NewSlot( SQSharedState *ss, const SQObjectPtr &key, const SQObjectPtr &val, bool bstatic ) {
  SQObjectPtr temp;
  if( _locked ) {
    return false;
  }
  if( _members->Get( key, temp ) && _isfield( temp ) ) {
    _defaultvalues[_member_idx( temp )].val = val;
    return true;
  }
  if( type( val ) == OT_CLOSURE || type( val ) == OT_NATIVECLOSURE || bstatic ) {
    SQInteger mmidx;
    if( ( type( val ) == OT_CLOSURE || type( val ) == OT_NATIVECLOSURE ) &&
        ( mmidx = ss->GetMetaMethodIdxByName( key ) ) != -1 ) {
      _metamethods[mmidx] = val;
    } else {
      if( type( temp ) == OT_NULL ) {
        SQClassMember m;
        m.val = val;
        _members->NewSlot( key, SQObjectPtr( _make_method_idx( _methods.size() ) ) );
        _methods.push_back( m );
      } else
      { _methods[_member_idx( temp )].val = val; }
    }
    return true;
  }
  SQClassMember m;
  m.val = val;
  _members->NewSlot( key, SQObjectPtr( _make_field_idx( _defaultvalues.size() ) ) );
  _defaultvalues.push_back( m );
  return true;
}

SQInstance *SQClass::CreateInstance() {
  if( !_locked ) {
    Lock();
  }
  return SQInstance::Create( _opt_ss( this ), this );
}

SQInteger SQClass::Next( const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval ) {
  SQObjectPtr oval;
  SQInteger idx = _members->Next( false, refpos, outkey, oval );
  if( idx != -1 ) {
    if( _ismethod( oval ) ) {
      outval = _methods[_member_idx( oval )].val;
    } else {
      SQObjectPtr &o = _defaultvalues[_member_idx( oval )].val;
      outval = _realval( o );
    }
  }
  return idx;
}

bool SQClass::SetAttributes( const SQObjectPtr &key, const SQObjectPtr &val ) {
  SQObjectPtr idx;
  if( _members->Get( key, idx ) ) {
    if( _isfield( idx ) ) {
      _defaultvalues[_member_idx( idx )].attrs = val;
    } else
    { _methods[_member_idx( idx )].attrs = val; }
    return true;
  }
  return false;
}

bool SQClass::GetAttributes( const SQObjectPtr &key, SQObjectPtr &outval ) {
  SQObjectPtr idx;
  if( _members->Get( key, idx ) ) {
    outval = ( _isfield( idx ) ? _defaultvalues[_member_idx( idx )].attrs : _methods[_member_idx( idx )].attrs );
    return true;
  }
  return false;
}

void SQInstance::Init( SQSharedState *ss ) {
  _userpointer = NULL;
  _hook = NULL;
  __ObjAddRef( _class );
  _delegate = _class->_members;
  INIT_CHAIN();
  ADD_TO_CHAIN( &_sharedstate->_gc_chain, this );
}

SQInstance::SQInstance( SQSharedState *ss, SQClass *c, SQInteger memsize ) {
  _memsize = memsize;
  _class = c;
  SQUnsignedInteger nvalues = _class->_defaultvalues.size();
  for( SQUnsignedInteger n = 0; n < nvalues; n++ ) {
    new( &_values[n] ) SQObjectPtr( _class->_defaultvalues[n].val );
  }
  Init( ss );
}

SQInstance::SQInstance( SQSharedState *ss, SQInstance *i, SQInteger memsize ) {
  _memsize = memsize;
  _class = i->_class;
  SQUnsignedInteger nvalues = _class->_defaultvalues.size();
  for( SQUnsignedInteger n = 0; n < nvalues; n++ ) {
    new( &_values[n] ) SQObjectPtr( i->_values[n] );
  }
  Init( ss );
}

void SQInstance::Finalize() {
  SQUnsignedInteger nvalues = _class->_defaultvalues.size();
  __ObjRelease( _class );
  for( SQUnsignedInteger i = 0; i < nvalues; i++ ) {
    _values[i] = _null_;
  }
}

SQInstance::~SQInstance() {
  REMOVE_FROM_CHAIN( &_sharedstate->_gc_chain, this );
  if( _class ) {
    Finalize();
  }
}

bool SQInstance::GetMetaMethod( SQVM *, SQMetaMethod mm, SQObjectPtr &res ) {
  if( type( _class->_metamethods[mm] ) != OT_NULL ) {
    res = _class->_metamethods[mm];
    return true;
  }
  return false;
}

bool SQInstance::InstanceOf( SQClass *trg ) {
  SQClass *parent = _class;
  while( parent != NULL ) {
    if( parent == trg ) {
      return true;
    }
    parent = parent->_base;
  }
  return false;
}
